﻿namespace Sidvall.Web.IO
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")]
    public class PartialStream : System.IO.Stream
    {
        #region Public Members

        #region CanRead

        public override bool CanRead
        {
            get
            {
                return true;
            }
        }

        #endregion
        #region CanSeek

        public override bool CanSeek
        {
            get
            {
                return true;
            }
        }

        #endregion
        #region CanWrite

        public override bool CanWrite
        {
            get
            {
                return false;
            }
        }

        #endregion
        #region Length

        public override long Length
        {
            get
            {
                return _InnerStream.Length;
            }
        }

        #endregion
        #region Position

        public override long Position
        {
            get
            {
                return _InnerStream.Position;
            }
            set
            {
                _InnerStream.Position = value;
            }
        }

        #endregion

        #region BeginRead

        public override System.IAsyncResult BeginRead(byte[] buffer, int offset, int count, System.AsyncCallback callback, object state)
        {
            int byteCountToRead;

            byteCountToRead = count;
            if (_Position + byteCountToRead > _End)
                byteCountToRead = (int)(_End - _Position);
            var result = _InnerStream.BeginRead(buffer, offset,
                           byteCountToRead, (s) =>
                           {
                               _Position += byteCountToRead;
                               callback(s);
                           }, state);
            return result;
        }

        #endregion
        #region EndRead

        public override int EndRead(System.IAsyncResult asyncResult)
        {
            return _InnerStream.EndRead(asyncResult);
        }

        #endregion
        #region Read

        public override int Read(byte[] buffer, int offset, int count)
        {
            var byteCountToRead = count;
            if (_Position + byteCountToRead > _End)
                byteCountToRead = (int)(_End - _Position);
            var result = _InnerStream.Read(buffer, offset, byteCountToRead);
            _Position += byteCountToRead;
            return result;
        }

        #endregion
        #region ReadByte

        public override int ReadByte()
        {
            int result = _InnerStream.ReadByte();
            _Position += 1;
            return result;
        }

        #endregion

        #region Dispose

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (_InnerStream != null)
                {
                    _InnerStream.Dispose();
                    _InnerStream = null;
                }
            }
            base.Dispose(disposing);
        }

        #endregion
        #region Flush

        public override void Flush()
        {
            _InnerStream.Flush();
        }

        #endregion
        #region Seek

        public override long Seek(long offset, System.IO.SeekOrigin origin)
        {
            switch (origin)
            {
                case System.IO.SeekOrigin.Begin:
                    _Position = _Start + offset;
                    return _InnerStream.Seek(_Start + offset, System.IO.SeekOrigin.Begin);
                case System.IO.SeekOrigin.Current:
                    _Position += offset;
                    return _InnerStream.Seek(offset, System.IO.SeekOrigin.Current);
                default:
                    throw new System.NotImplementedException("Seeking from SeekOrigin.End is not implemented");
            }
        }

        #endregion

        #endregion
        #region Private Members

        private System.IO.Stream _InnerStream;
        private long _Start;
        private long _Position;
        private long _End;

        #endregion
        #region Constructors

        public PartialStream(System.IO.Stream stream, ByteRange item)
            : this(stream, item.From, item.To + 1)
        {
        }
        public PartialStream(System.IO.Stream stream, long start, long? end)
        {
            if (stream == null)
                throw new System.ArgumentNullException("stream");
            _InnerStream = stream;
            _Start = start;
            _Position = start;
            if (end != null)
                _End = end.Value;
            else
                _End = _InnerStream.Length;
            if (_Start > 0)
                _InnerStream.Seek(_Start, System.IO.SeekOrigin.Begin);
        }

        #endregion

        #region Not Implemented

        public override void SetLength(long value)
        {
            throw new System.NotImplementedException();
        }
        public override void Write(byte[] buffer, int offset, int count)
        {
            throw new System.NotImplementedException();
        }

        #endregion
    }
}
